<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

</body>
<script>
    // 函数，使用functio声明的函数，一般写在全局作作用域中
    // function fn() {}
    // 方法：通常是指对象的一个属性，该属性的值是一个函数，函数为匿名函数
    //===========================================================================================================//
    // this指向
    // -------------------------------------------------------
    // // 1-默认绑定函数调用：this指向window全局对象
    // // var a = 1;
    // // function fn() {
    // //     var a = 2;
    // //     console.log(this.a);
    // // }
    // //  默认绑定
    // var name = 'jj';
    // var a = {
    //     name: 'jay',
    //     music: function () {
    //         console.log(this.name);

    //     }
    // }
    // // b作为函数独立调用
    // var b = a.music;
    // b()//jj
    // a.music();//jay


    // -------------------------------------------------------

    // // 2-隐式绑定方法调用：this指向的是调用的那个对象
    // var name = '周杰伦'
    // var user = {
    //     name: '陈冠希',
    //     cool: function () {
    //         console.log(this);//this指向的方法所在的对象
    //         console.log(this.name);//陈冠希
    //     }
    // }
    // user.cool();

    // -------------------------------------------------------

    // // 3-new绑定：构造函数
    // // 构造函数里的this指向的是创建的实例
    // function Person(name) {
    //     this.name = name;
    //     console.log(this);//this指向的就是下面的实例p
    // }
    // var p = new Person('love');
    // console.log(p.name);//love
    // Person();//Window


    // -------------------------------------------------------

    // // 4-显示绑定：call、apply、bind
    // // 这三个方法都可以手动改变this的指向
    // // call和apply的区别：call（后面的参数是一个参数列表）接受参数列表（以逗号分割），apply（第二个参数是一个数组）接受参数数组
    // // 应用：可以使用call、apply、借用原本不能直接使用的方法

    // function fn() {
    //     console.log(arguments);
    //     console.log([1, 2, 3, 4, 5].slice(1));
    //     console.log([].slice.call(arguments, 1));

    // }
    // fn(1, 2, 3, 4, 5);






    // //例子1
    // // 父类
    // function One(name, music) {
    //     this.name = name;
    //     this.music = music;
    // }
    // // 子类
    // function Two(type, name, music) {
    //     this.type = type;
    //     // call的作用：让this指向One的实例，并传入两个参数，让this也拥有One实例上的两个属性
    //     One.call(this, name, music);

    // }
    // var a = new Two('歌手', '周杰伦', '东风破');
    // console.log(a);






    // // 例子2
    // function fn() {
    //     console.log(this, arguments);

    // }
    // fn();
    // // call、apply将this指向第一个参数，第二个参数就是传入函数的参数
    // fn.call({ name: 'jay', music: '菊花台' }, 1, 2, 3)
    // fn.apply({ name: 'jay', music: '菊花台' }, [1, 2, 3])
    // //懒执行
    // // 仅仅只是把this指向了第一个参数，不会执行函数，传参的方式和call一样
    // fn.bind({ name: 'jay', music: '菊花台' }, 1, 2, 3)





    // 面试题：call、apply、bind的异同点
    // 参考答案：
    // 相同点：都是用来改变this指向
    // 不同点：a.执行时机：call/apply是立即执行，bind是懒执行
    //        b.传参形式：call/bind传的参数是参数列表，apply传的是数组
    //        c.call和apply可以让对象使用原本不能使用的对象，bind仅仅只能改变this的指向
    // -------------------------------------------------------
    // // 5-箭头函数

    // // 箭头函数可以捕获其所在上下文的this值
    // function Person() {
    //     this.age = 0;
    //     setInterval(() => {
    //         // 回调里面的 `this` 变量就指向了期望的那个对象了
    //         this.age++;
    //     }, 3000);
    // }
    // var p = new Person();
    // console.log(p);



    // // 作为方法的箭头函数this指向全局window对象，而普通函数的this则指向调用它的对象
    // i = 1
    // var obj = {
    //     i: 2,
    //     b: () => console.log(this.i, this),
    //     c: function () {
    //         console.log(this.i, this)
    //     }
    // }
    // obj.b();  // 1 window{...}
    // obj.c();  // 2 Object {...}

    //===========================================================================================================//
    //  手写方法
    // // call
    // function fn() {
    //     console.log(this, arguments);
    // }
    // Function.prototype.myCall = function () {
    //     console.log(arguments);
    //     var a = Array.prototype.slice.apply(arguments);
    //     var context = a[0] || window;
    //     var b = a.slice(1);
    //     context.fn = this;//把当前函数保存为context的一个属性方法
    //     context.fn(...b);//调用保存的属性方法
    //     delete context.fn//执行后删除context保存的属性方法


    // }
    // fnn.myCall({ name: 'jay' }, 11, 22, 33)
    // fn.myCall(null, 11, 22, 33)






    // -------------------------------------------------------

    // // apply
    // function fnn() {
    //     console.log(this, arguments);
    // }
    // Function.prototype.myApply = function () {
    //     console.log(arguments);
    //     var a = Array.prototype.slice.apply(arguments);
    //     var context = a[0] || window;
    //     var b = a[1];//把第二个参数取出来
    //     context.fnn = this;//把当前函数保存为context的一个属性方法
    //     context.fnn(...b);//调用保存的属性方法
    //     delete context.fn//执行后删除context保存的属性方法

    // }
    // fnn.myApply({ name: 'jay' }, [11, 22, 33])






    // -------------------------------------------------------

    // // bind
    // // bind方法内部返回的是一个函数
    // function fns() {
    //     console.log(this, arguments);

    // }
    // fns.bind({ name: 'jay' }, 11, 22, 33)
    // fns.bind({ name: 'jay' }, 11, 22, 33)()
    // Function.prototype.myBind = function () {
    //     var a = Array.prototype.slice.call(arguments)
    //     var context = a[0];
    //     var b = a.slice(1)
    //     // es6
    //     return () => {
    //         var c = Array.prototype.slice.call(arguments)
    //         return this.apply(context, b.concat(c));
    //     }
    // }
    // fns.myBind({ name: 'jay' }, 11, 22, 33)





    // -------------------------------------------------------

    // // new
    // function fn(name, music) {
    //     this.name = name;
    //     this.music = music;
    // }
    // function myNew(fn, ...a) {
    //     // 创建一个具有指定原型的空对象
    //     var obj = Object.create(fn.prototype);
    //     // 2.把this指向这个空对象并执行构造函数里的代码
    //     var b = fn.apply(obj, a);
    //     // 返回这个对象
    //     return obj

    // }
    // var c = myNew(fn, 'jay', '七里香')
    // console.log(c);



    //===========================================================================================================//
    // 递归
    // 一定要有出口找边界条件，变化量应作为参数，找重复// -------------------------------------------------------
    // 案例
    // // 1-n的累加
    // function sum(n)  {
    //     if (n == 1) return 1
    //     return n + sum(n - 1);

    // }
    // console.log(sum(10));


    // -------------------------------------------------------
    // 案例
    // // 阶乘
    // function sum(n) {
    //     if (n == 1) return 1
    //     return n * sum(n - 1);
    // }
    // console.log(sum(10));




    //===========================================================================================================//
























</script>

</html>